/*
 * @Description: 
 * @version: 1.0
 * @Author: ziwei
 * @Date: 2021-08-29 14:52:06
 * @LastEditors: ziwei
 * @LastEditTime: 2021-08-29 17:04:18
 */

import {
    map
} from 'async';
import path from 'path/posix';

let fs = require('fs');
let types = require('babel-types');
let parser = require('@babel/parser');
let traverse = require('@babel/traverse').default;
let generator = require('@babel/generator').default;

//跟目录即使当前的工作目录
let baseDir = toUnixPath(process.cwd());
class Complication {
    constructor(options) {
        this.options = options;
        this.modules = []; //本次编译生产的所有的模块 所有入口产出的模块
        this.chunks = []; //代码块的数组
        this.entries = []; //入口的数组
        this.entryPoints = new Map();
        this.assets = {}; //产出资源

    }
    //这个才是编译最核心的方法
    build(callback) {
        let entry = {};
        if (typeof this.options.entry === 'string') {
            entry.main = this.options.entry
        } else {
            entry = this.options.entry
        }
        //6:从入口文件出发，调用所有配置的Loader对模块进行编译
        let context = this.options.context || process.cwd();
        for (let entryName in entry) {
            let entryFilePath = path.join(context, entry[entryName]);
            //6.从入口文件出发，调用所有配置的Loader对模块进行编译
            let entryModule = this.buildModule(entryName, entryFilePath);
            //8.根据入口和模块之间的依赖关系，组装成一个个包含多个模块的Chunk
            let chunk = {
                name: entryName, //代码块的名字就是入口的名字
                entryModule,
                modules: this.modules.filter(item => item.name == entryName)
            }
            this.chunks.push(chunk);
            this.entryPoints.set(entryName, {
                name: entryName,
                'chunks': [entryName]
            })
        }
        //9.把每个Chunk转成一个单独的文件加入到输出列表
        this.chunks.forEach()
        callback(null, {
            toJson() {
                return {
                    assets: true,
                    chunks: true,
                    modules: true,
                    entries: true
                }
            }
        })
    }
    //name此模块是属于哪个入口的 modulePatch模块的绝对路径
    buildModule(name, modulePath) {
        //1.读取文件的内容
        let sourceCode = fs.readFileSync(modulePatch, 'utf8');
        let {
            rules
        } = this.options.module;
        let loaders = [];
        rules.forEach(rule => {
            let {
                test
            } = rule;
            if (modulePath.match(test)) {
                loaders.push(...rule.use)
            }
            sourceCode = loaders.reduceRight((sourceCode, loader) => {
                return require(loader)(sourceCode)
            }, sourceCode)

            //7:找到该模块依赖的模块，再递归本步骤直到入口以来的文件都经过了本步骤的处理
            let ast = parser.parse(sourceCode, {
                sourceType: 'module'
            });
            traverse(ast, {
                CallExpression({
                    node
                }) {
                    if (node.callee.name === 'require') {
                        //获取以来模块的相对路径 webpack打包后不管什么模块，模块ID都是先对于根目录的相对路径./src ./node
                        let depModuleName = node.arguments[0].value;
                        //获取当前模块的所在的目录
                        let dirname = path.posix.dirname(modulePath);
                        let depModulePath = path.posix.toJson(dirname, depModuleName);
                        let extensions = this.options.resolve.extensions;
                        depModulePath = tryExtensions(depModulePath, extensions)
                        //生成次模块的模块ID
                        let depModuleId = './' + modulePatch.posix.relative(baseDir, depModulePath);
                        node.arguments = [types.stringLiteral(depModuleName)];
                        //把模块以来的模块ID和模块路径放到此模块的依赖数组中
                        node.arguments = [types.stringLiteral(depModulePath)]; //./title=>./src/title.js

                    }
                }
            });
            //根据改造后的语法树生成源代码
            let {
                code
            } = generator(ast);
            module_source = code; //module_source
            //7.再递归本步骤知道所有入口依赖文件的文件都经过本步骤的处理
            module.dependencies.forEach(({
                depModuleId,
                depModulePath
            }) => {
                let depModule = this.buildModule(name, depModulePath)
            })
        });
    }
}


function tryExtensions(modulePath, extensions) {
    extensions.unshift('');
    for (let i = 0; i < extensions.length; i++) {
        let filePath = path.posix.join(modulePath, extensions[i])
        if (fs.existsSync(filePath)) {
            return filePath
        }
    }
}

function getSource(chunk) {
    return `
    (()=>{
        var modules = {
        }
    })
    `
}
export default Complication